Įvaldykite pytest fixtures efektyviam ir lengvai prižiūrimam testavimui. Sužinokite priklausomybių įpurškimo principus ir praktinius pavyzdžius, kaip rašyti patikimus testus.
Pytest Fixtures: Priklausomybių įpurškimas patikimam testavimui
Programinės įrangos kūrimo srityje patikimas ir patikimas testavimas yra itin svarbus. „Pytest“, populiarus „Python“ testavimo sistema, siūlo galingą funkciją, vadinamą fixtures, kuri supaprastina testų nustatymą ir nuėmimą, skatina kodo pakartotinį naudojimą ir padidina testų priežiūrą. Šiame straipsnyje gilinamasi į pytest fixtures koncepciją, nagrinėjant jų vaidmenį priklausomybių įpurškime ir pateikiant praktinius pavyzdžius, iliustruojančius jų efektyvumą.
Kas yra Pytest Fixtures?
Iš esmės „pytest fixtures“ yra funkcijos, kurios suteikia fiksuotą bazinę liniją testams, kad būtų patikimai ir pakartotinai vykdomi. Jie veikia kaip priklausomybių įpurškimo mechanizmas, leidžiantis apibrėžti pakartotinai naudojamus išteklius ar konfigūracijas, kurias lengvai pasiekia kelios testavimo funkcijos. Pagalvokite apie juos kaip apie gamyklas, kurios paruošia aplinką, kurioje jūsų testai turi veikti tinkamai.
Skirtingai nei tradiciniai nustatymo ir nuėmimo metodai (pvz., setUp
ir tearDown
unittest
), „pytest fixtures“ siūlo didesnį lankstumą, moduliškumą ir kodo organizavimą. Jie leidžia aiškiai apibrėžti priklausomybes ir valdyti jų gyvavimo ciklą aiškiu ir glaustu būdu.
Priklausomybių įpurškimas paaiškintas
Priklausomybių įpurškimas yra dizaino šablonas, kai komponentai gauna priklausomybes iš išorinių šaltinių, o ne patys jas kuria. Tai skatina laisvą sukabinimą, todėl kodas yra moduliaresnis, testuojamas ir lengvai prižiūrimas. Testavimo kontekste priklausomybių įpurškimas leidžia lengvai pakeisti tikras priklausomybes maketo objektais arba testiniais dubliais, todėl galite izoliuoti ir išbandyti atskirus kodo vienetus.
„Pytest fixtures“ sklandžiai palengvina priklausomybių įpurškimą, suteikdami mechanizmą testavimo funkcijoms deklaruoti savo priklausomybes. Kai testavimo funkcija prašo „fixture“, „pytest“ automatiškai vykdo „fixture“ funkciją ir įterpia jos grąžinimo reikšmę į testavimo funkciją kaip argumentą.
„Pytest Fixtures“ naudojimo pranašumai
„Pytest fixtures“ naudojimas testavimo darbo eigoje suteikia daugybę privalumų:
- Kodo pakartotinis naudojimas: „Fixtures“ gali būti pakartotinai naudojami keliose testavimo funkcijose, pašalinant kodo dubliavimą ir skatinant nuoseklumą.
- Testo priežiūra: Priklausomybių pakeitimus galima atlikti vienoje vietoje (fixture apibrėžime), sumažinant klaidų riziką ir supaprastinant priežiūrą.
- Pagerintas skaitomumas: „Fixtures“ daro testavimo funkcijas skaitomesnes ir labiau susikoncentravusias, nes jose aiškiai deklaruojamos jų priklausomybės.
- Supaprastintas nustatymas ir nuėmimas: „Fixtures“ automatiškai tvarko nustatymo ir nuėmimo logiką, sumažindami šablono kodą testavimo funkcijose.
- Parametrizavimas: „Fixtures“ gali būti parametrizuotos, leidžiančios paleisti testus su skirtingais įvesties duomenų rinkiniais.
- Priklausomybių valdymas: „Fixtures“ suteikia aiškų ir aiškų būdą valdyti priklausomybes, todėl lengviau suprasti ir kontroliuoti testavimo aplinką.
Pagrindinis „Fixture“ pavyzdys
Pradėkime nuo paprasto pavyzdžio. Tarkime, kad jums reikia išbandyti funkciją, kuri sąveikauja su duomenų baze. Galite apibrėžti „fixture“, kad sukurtumėte ir sukonfigūruotumėte duomenų bazės ryšį:
import pytest
import sqlite3
@pytest.fixture
def db_connection():
# Setup: create a database connection
conn = sqlite3.connect(':memory:') # Use an in-memory database for testing
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT,
email TEXT
)
""")
conn.commit()
# Provide the connection object to the tests
yield conn
# Teardown: close the connection
conn.close()
def test_add_user(db_connection):
cursor = db_connection.cursor()
cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)", ('John Doe', 'john.doe@example.com'))
db_connection.commit()
cursor.execute("SELECT * FROM users WHERE name = ?", ('John Doe',))
result = cursor.fetchone()
assert result is not None
assert result[1] == 'John Doe'
assert result[2] == 'john.doe@example.com'
Šiame pavyzdyje:
@pytest.fixture
dekoratorius žymidb_connection
funkciją kaip „fixture“.- „Fixture“ sukuria atmintyje esantį „SQLite“ duomenų bazės ryšį, sukuria
users
lentelę ir suteikia ryšio objektą. yield
sakinys atskiria sąrankos ir nuėmimo fazes. Kodas priešyield
vykdomas prieš testą, o kodas poyield
vykdomas po testo.test_add_user
funkcija prašodb_connection
„fixture“ kaip argumentą.- „Pytest“ automatiškai vykdo
db_connection
„fixture“ prieš vykdant testą, pateikdamas duomenų bazės ryšio objektą testavimo funkcijai. - Baigus testą, „pytest“ vykdo nuėmimo kodą „fixture“, uždarydamas duomenų bazės ryšį.
„Fixture“ aprėptis
„Fixtures“ gali turėti skirtingas sritis, kurios nustato, kaip dažnai jos vykdomos:
- funkcija (numatytoji): „Fixture“ vykdoma vieną kartą vienai testavimo funkcijai.
- klasė: „Fixture“ vykdoma vieną kartą vienai testavimo klasei.
- modulis: „Fixture“ vykdoma vieną kartą vienam moduliui.
- sesija: „Fixture“ vykdoma vieną kartą vienai testavimo sesijai.
Galite nurodyti „fixture“ sritį naudodami parametrą scope
:
import pytest
@pytest.fixture(scope="module")
def module_fixture():
# Setup code (executed once per module)
print("Module setup")
yield
# Teardown code (executed once per module)
print("Module teardown")
def test_one(module_fixture):
print("Test one")
def test_two(module_fixture):
print("Test two")
Šiame pavyzdyje module_fixture
vykdomas tik vieną kartą per modulį, nepaisant to, kiek testavimo funkcijų jo prašo.
„Fixture“ parametrizavimas
„Fixtures“ gali būti parametrizuotos, kad būtų vykdomi testai su skirtingais įvesties duomenų rinkiniais. Tai naudinga testuojant tą patį kodą su skirtingomis konfigūracijomis ar scenarijais.
import pytest
@pytest.fixture(params=[1, 2, 3])
def number(request):
return request.param
def test_number(number):
assert number > 0
Šiame pavyzdyje number
„fixture“ yra parametrizuotas su reikšmėmis 1, 2 ir 3. test_number
funkcija bus vykdoma tris kartus, po vieną kartą kiekvienai number
„fixture“ reikšmei.
Taip pat galite naudoti pytest.mark.parametrize
tiesiogiai parametrizuoti testavimo funkcijas:
import pytest
@pytest.mark.parametrize("number", [1, 2, 3])
def test_number(number):
assert number > 0
Tai pasiekia tą patį rezultatą kaip ir naudojant parametrizuotą „fixture“, bet dažnai yra patogiau paprastais atvejais.
Naudojant `request` objektą
`request` objektas, prieinamas kaip argumentas „fixture“ funkcijose, suteikia prieigą prie įvairios kontekstinės informacijos apie testavimo funkciją, kuri prašo „fixture“. Tai yra `FixtureRequest` klasės egzempliorius ir leidžia „fixtures“ būti dinamiškesniems ir pritaikomiems skirtingiems testavimo scenarijams.
Dažniausi `request` objekto naudojimo atvejai yra:
- Prieiga prie testavimo funkcijos pavadinimo:
request.function.__name__
pateikia testavimo funkcijos, kuri naudoja „fixture“, pavadinimą. - Prieiga prie modulio ir klasės informacijos: Galite pasiekti modulį ir klasę, kuriame yra testavimo funkcija, naudodami atitinkamai
request.module
irrequest.cls
. - Prieiga prie „Fixture“ parametrų: Naudojant parametrizuotas „fixtures“,
request.param
suteikia prieigą prie dabartinės parametro reikšmės. - Prieiga prie komandų eilutės parinkčių: Galite pasiekti „pytest“ perduotas komandų eilutės parinktis naudodami
request.config.getoption()
. Tai naudinga konfigūruojant „fixtures“ pagal vartotojo nustatytus nustatymus. - Finalizatorių pridėjimas:
request.addfinalizer(finalizer_function)
leidžia užregistruoti funkciją, kuri bus vykdoma pasibaigus testavimo funkcijai, neatsižvelgiant į tai, ar testas praėjo, ar nepraėjo. Tai naudinga valymo užduotims, kurios turi būti atliktos visada.
Pavyzdys:
import pytest
@pytest.fixture(scope="function")
def log_file(request):
test_name = request.function.__name__
filename = f"log_{test_name}.txt"
file = open(filename, "w")
def finalizer():
file.close()
print(f"\nClosed log file: {filename}")
request.addfinalizer(finalizer)
return file
def test_with_logging(log_file):
log_file.write("This is a test log message\n")
assert True
Šiame pavyzdyje log_file
„fixture“ sukuria žurnalo failą, būdingą testavimo funkcijos pavadinimui. finalizer
funkcija užtikrina, kad žurnalo failas būtų uždarytas po testo pabaigos, naudodama request.addfinalizer
, kad užregistruotų valymo funkciją.
Bendri „Fixture“ naudojimo atvejai
„Fixtures“ yra universalūs ir gali būti naudojami įvairiuose testavimo scenarijuose. Čia yra keletas bendrų naudojimo atvejų:
- Duomenų bazės jungtys: Kaip parodyta ankstesniame pavyzdyje, „fixtures“ gali būti naudojami duomenų bazės ryšiams sukurti ir valdyti.
- API klientai: „Fixtures“ gali kurti ir konfigūruoti API klientus, teikdami nuoseklią sąsają, skirtą sąveikai su išorinėmis paslaugomis. Pavyzdžiui, testuodami e. prekybos platformą visame pasaulyje, galite turėti „fixtures“ skirtingiems regioniniams API galiniams taškams (pvz., `api_client_us()`, `api_client_eu()`, `api_client_asia()`).
- Konfigūracijos nustatymai: „Fixtures“ gali įkelti ir pateikti konfigūracijos nustatymus, leidžiančius testams veikti su skirtingomis konfigūracijomis. Pavyzdžiui, „fixture“ gali įkelti konfigūracijos nustatymus pagal aplinką (kūrimo, testavimo, gamybos).
- Maketo objektai: „Fixtures“ gali sukurti maketo objektus arba testinius dublius, leidžiančius izoliuoti ir išbandyti atskirus kodo vienetus.
- Laikini failai: „Fixtures“ gali kurti laikinus failus ir katalogus, suteikdami švarią ir izoliuotą aplinką failų testams. Apsvarstykite galimybę išbandyti funkciją, kuri apdoroja vaizdo failus. „Fixture“ galėtų sukurti pavyzdinių vaizdo failų (pvz., JPEG, PNG, GIF) rinkinį su skirtingomis savybėmis, kad testas galėtų naudoti.
- Vartotojo autentifikavimas: „Fixtures“ gali tvarkyti vartotojo autentifikavimą, kad būtų galima išbandyti žiniatinklio programas ar API. „Fixture“ gali sukurti vartotojo paskyrą ir gauti autentifikavimo raktą, kad būtų galima naudoti vėlesniuose testuose. Testuojant daugiakalbes programas, „fixture“ galėtų sukurti autentifikuotus vartotojus su skirtingomis kalbos nuostatomis, siekiant užtikrinti tinkamą lokalizavimą.
Pažangūs „Fixture“ metodai
„Pytest“ siūlo keletą pažangių „fixture“ metodų, kad padidintumėte testavimo galimybes:
- „Fixture Autouse“: Galite naudoti parametrą
autouse=True
, kad „fixture“ būtų automatiškai pritaikyta visoms testavimo funkcijoms modulyje ar sesijoje. Naudokite tai atsargiai, nes aiškios priklausomybės gali apsunkinti testų supratimą. - „Fixture“ vardų erdvės: „Fixtures“ apibrėžiamos vardų erdvėje, kurią galima naudoti norint išvengti pavadinimų konfliktų ir organizuoti „fixtures“ į logines grupes.
- „Fixtures“ naudojimas „Conftest.py“: „Fixtures“, apibrėžtos
conftest.py
, automatiškai pasiekiamos visoms testavimo funkcijoms tame pačiame kataloge ir jo pakatalogiuose. Tai gera vieta bendrai naudojamoms „fixtures“ apibrėžti. - „Fixtures“ bendrinimas tarp projektų: Galite sukurti pakartotinai naudojamas „fixture“ bibliotekas, kurios gali būti bendrinamos keliuose projektuose. Tai skatina kodo pakartotinį naudojimą ir nuoseklumą. Apsvarstykite galimybę sukurti bendrų duomenų bazės „fixtures“ biblioteką, kurią galima naudoti keliose programose, sąveikaujančiose su ta pačia duomenų baze.
Pavyzdys: API testavimas su „Fixtures“
Paaiškinkime API testavimą su „fixtures“ naudodami hipotetinį pavyzdį. Tarkime, kad testuojate API pasaulinei e. prekybos platformai:
import pytest
import requests
BASE_URL = "https://api.example.com"
@pytest.fixture
def api_client():
session = requests.Session()
session.headers.update({"Content-Type": "application/json"})
return session
@pytest.fixture
def product_data():
return {
"name": "Global Product",
"description": "A product available worldwide",
"price": 99.99,
"currency": "USD",
"available_countries": ["US", "EU", "Asia"]
}
def test_create_product(api_client, product_data):
response = api_client.post(f"{BASE_URL}/products", json=product_data)
assert response.status_code == 201
data = response.json()
assert data["name"] == "Global Product"
def test_get_product(api_client, product_data):
# First, create the product (assuming test_create_product works)
response = api_client.post(f"{BASE_URL}/products", json=product_data)
product_id = response.json()["id"]
# Now, get the product
response = api_client.get(f"{BASE_URL}/products/{product_id}")
assert response.status_code == 200
data = response.json()
assert data["name"] == "Global Product"
Šiame pavyzdyje:
api_client
„fixture“ sukuria pakartotinai naudojamą užklausų sesiją su numatytuoju turinio tipu.product_data
„fixture“ pateikia pavyzdinį produkto krūvį kuriant produktus.- Testai naudoja šiuos „fixtures“ norėdami kurti ir gauti produktus, užtikrindami švarią ir nuoseklią API sąveiką.
Geriausia praktika naudojant „Fixtures“
Norėdami maksimaliai padidinti „pytest“ „fixtures“ naudą, laikykitės šios geriausios praktikos:
- Laikykite „Fixtures“ mažas ir susitelkęs: Kiekvienas „fixture“ turėtų turėti aiškų ir konkretų tikslą. Venkite kurti pernelyg sudėtingas „fixtures“, kurios daro per daug.
- Naudokite prasmingus „Fixture“ pavadinimus: Pasirinkite aprašomuosius savo „fixtures“ pavadinimus, kurie aiškiai nurodo jų paskirtį.
- Venkite šalutinių poveikių: „Fixtures“ turėtų būti orientuoti į išteklių nustatymą ir teikimą. Venkite atlikti veiksmus, kurie galėtų turėti nepageidaujamą šalutinį poveikį kitiems testams.
- Dokumentuokite savo „Fixtures“: Pridėkite docstrings į savo „fixtures“, kad paaiškintumėte jų paskirtį ir naudojimą.
- Naudokite „Fixture“ sritis tinkamai: Pasirinkite tinkamą „fixture“ sritį, atsižvelgdami į tai, kaip dažnai „fixture“ reikia vykdyti. Nenaudokite sesijos apimties „fixture“, jei pakaks funkcijos apimties „fixture“.
- Apsvarstykite testų izoliaciją: Įsitikinkite, kad jūsų „fixtures“ užtikrina pakankamą izoliaciją tarp testų, kad būtų išvengta trukdžių. Pavyzdžiui, naudokite atskirą duomenų bazę kiekvienai testavimo funkcijai ar moduliui.
Išvada
„Pytest fixtures“ yra galingas įrankis rašant patikimus, lengvai prižiūrimus ir efektyvius testus. Priėmę priklausomybių įpurškimo principus ir pasinaudoję „fixtures“ lankstumu, galite žymiai pagerinti savo programinės įrangos kokybę ir patikimumą. Nuo duomenų bazės ryšių valdymo iki maketo objektų kūrimo, „fixtures“ suteikia švarų ir organizuotą būdą tvarkyti testų nustatymą ir nuėmimą, todėl testavimo funkcijos tampa skaitomesnės ir labiau susikoncentravusios.
Laikydamiesi šiame straipsnyje aprašytos geriausios praktikos ir tyrinėdami pažangius metodus, galite išnaudoti visą „pytest fixtures“ potencialą ir pagerinti savo testavimo galimybes. Nepamirškite teikti pirmenybę kodo pakartotiniam naudojimui, testų izoliacijai ir aiškiai dokumentacijai, kad sukurtumėte testavimo aplinką, kuri būtų efektyvi ir lengvai prižiūrima. Kai ir toliau integruosite „pytest fixtures“ į savo testavimo darbo eigą, pastebėsite, kad jie yra nepakeičiamas turtas kuriant aukštos kokybės programinę įrangą.
Galų gale, „pytest fixtures“ įvaldymas yra investicija į jūsų programinės įrangos kūrimo procesą, didinanti pasitikėjimą jūsų kodu ir sklandesnį kelią į patikimų ir tvirtų programų pateikimą vartotojams visame pasaulyje.